package project3;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import project2.ProxyServer;

public class ProxyServerWrapper {
	private static final int ARRAY_LENTH = 512;
	private static final int TIME_OUT = 5;
	private static final int TIME_OUT_INTERVAL = 500;
	private int port; // The port which the ProxyServer binds to.
	private static Map<Integer, byte[]> bufferPool;
	private static List<Integer> activeStreamId;
	
	// This soc is created when sending message to the proxy server.
	private static Socket soc;
	private static DataInputStream input;
	private static DataOutputStream output;
	
	/**
	 * Throws Exception if error happens when connecting to the ProxyServer. 
	 * 
	 * @param port
	 * @throws Exception
	 */
	public ProxyServerWrapper(int port) throws Exception {
		this.port = port;
		if (bufferPool == null) {
			// Intialize a ProxyServer.
			String[] args = new String[2];
			args[0] = port + "";
			args[1] = "-n";
			ProxyServer.main(args);
			
			this.bufferPool = new ConcurrentHashMap<Integer, byte[]>();
			
			// Create listen thread to put new response from ProxyServer to buffer pool
			this.soc = new Socket("localhost", port);
			this.input = (DataInputStream) soc.getInputStream();
			this.output = (DataOutputStream) soc.getOutputStream();
		}
	}
	
	public static byte[] handleRequest(final int streamid, final byte[] httpRequest) throws Exception {
		new Thread() {
			
			@Override
			public void run() {
				synchronized (soc) {
					try {
						output.write(httpRequest);
						
						// Read the input data from the ProxyServer
						List<byte[]> responses = new LinkedList<byte[]>();
						byte[] temp = new byte[ARRAY_LENTH];
						while (input.read(temp) == ARRAY_LENTH) {
							responses.add(temp);
							temp = new byte[ARRAY_LENTH];
						}
						
						// Put this into the bufferPool
						byte[] response = new byte[ARRAY_LENTH * responses.size()];
						for (int i = 0; i < responses.size(); i++) {
							for (int j = 0; j < ARRAY_LENTH; j++) {
								response[i * 512 + j] = responses.get(i)[j];
							}
						}
						if (activeStreamId.contains(streamid)) {
							bufferPool.put(streamid, response);
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			
		}.run();
		
		return response(streamid);
	}
	
	private static byte[] response(Integer streamid) {
		activeStreamId.add(streamid);
		
		// Timing outtttttttt? ouch... it's time out.
		for (int i = 0; i < TIME_OUT; i++) {
			try {
				Thread.sleep(TIME_OUT_INTERVAL);
			} catch (InterruptedException e) {
				// TODO: Handle error when timing out failure
				e.printStackTrace();
			}
			if (bufferPool.containsKey(streamid)) {
				return bufferPool.get(streamid);
			}
		}
		
		activeStreamId.remove(streamid);
		
		// TODO: Handle time out message.
		return null;
	}
	
	// Register the map from the stream id to the hostname:port if we can connect to the hostname:port
	public static boolean registerStreamId() {
		return true;
	}
}
